﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace CubeDetection
{
  
   public class RasterCubeFilter
   {
      public event EventHandler OnPreviewChanged;

      private void InvokeOnPreviewChanged()
      {
         EventHandler onPreviewChangedHandler = OnPreviewChanged;
         if (onPreviewChangedHandler != null) onPreviewChangedHandler(this, EventArgs.Empty);
      }

      protected Bitmap bmp;
      protected CubeInfo[] Cubes = new CubeInfo[9];
      protected float maxDistance = 50f;
      protected Rectangle roi;
      protected Bitmap ScanBmp;


      public Bitmap Bitmap
      {
         get { return bmp; }
         set
         {
            if (value == null)
               return;
            bmp = value;
            if (roi.Width == 0 || roi.Right > value.Width)
               roi.Width = value.Width - roi.X;
            if (roi.Height == 0 || roi.Bottom > value.Height)
               roi.Height = value.Height - roi.Y;
            CalcRectangles();
            InvokeOnPreviewChanged();
         }
      }

      private int borderWidth;
      public int BorderWidth
      {
         get { return borderWidth; }
         set { borderWidth = value;
            CalcRectangles();
            InvokeOnPreviewChanged();
         }
      }

      public float MaxDistance
      {
         get { return maxDistance; }
         set { maxDistance = value; }
      }

      private int padding;
      public int Padding
      {
         get { return padding; }
         set
         {
            padding = value;
            CalcRectangles();
            InvokeOnPreviewChanged();
         }
      }

      public Rectangle ROI
      {
         get { return roi; }
         set
         {
            if (roi.Equals(value))
               return;
            roi = value;
            CalcRectangles();
            InvokeOnPreviewChanged();
         }
      }
      void CalcRectangles()
      {
         int width = roi.Width - 2*BorderWidth - 6*padding;
         width /= 3;
         width = Math.Max(0, width);

         int height = roi.Height - 2 * BorderWidth - 6 * padding;
         height /= 3;
         height = Math.Max(0, height);

         int xOffset = roi.X;
         int yOffset = roi.Y;

         for (int i = 0; i < 3; i++)
         {
            for (int j = 0; j < 3; j++)
            {
               Cubes[i * 3 + j] = new CubeInfo();
               Cubes[i * 3 + j].Cube = new Rectangle(
                  xOffset //Offset
                  +j*(width+BorderWidth) //Border,
                  +(1+2*j)*padding, //Padding
                  yOffset //Offset
                  +i*(height+BorderWidth) // Border
                  +(1+2*i)*padding, //Padding
                  width,
                  height);
            }
         }
      }

      public Bitmap Preview
      {
         get
         {
            if(bmp == null)
               return null;
            var result = (Bitmap) bmp.Clone();
            DrawRoi(ref result);
            return result;
         }
      }

      protected void DrawRoi(ref Bitmap bitmap)
      {
         Graphics g = Graphics.FromImage(bitmap);

         Pen dashedPen = new Pen(Color.Black);
         dashedPen.DashStyle = DashStyle.Dash;
         GetCubeColors();
         Color CubeColor;
         for (int i = 0; i < 9; i++)
         {
            //g.DrawRectangle(Pens.Black, Cubes[i].Cube.X + 1, Cubes[i].Cube.Y + 1, Cubes[i].Cube.Width, Cubes[i].Cube.Height);
            g.DrawRectangle(dashedPen, Cubes[i].Cube);
            DrawHalfRect(g, Cubes[i].Cube, new SolidBrush(Cubes[i].Color), dashedPen);

            if (FindCubeSidebyColor(Cubes[i].Color, out CubeColor))
            {  
               Rectangle rect = Cubes[i].Cube;
               rect.X += rect.Width /4;
               rect.Y += rect.Height / 2;
               rect.Width /= 2;
               rect.Height /= 4;
               g.FillRectangle(new SolidBrush(CubeColor), rect);
               g.DrawRectangle(dashedPen, rect);
             }
         }
         g.FillRectangle(new SolidBrush(Color.FromArgb(128,0,0,0)), 0, 0, bitmap.Width, roi.Y);
         g.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), 0, roi.Bottom, bitmap.Width, bitmap.Height - roi.Bottom);
         g.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), 0, roi.Y, roi.Left, roi.Height);
         g.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), roi.Right, roi.Y, bitmap.Width - roi.Right, roi.Height);
      
      }

      public static void DrawCross(Graphics g, Pen p, int x, int y, int w, int h, int size)
      {
         g.DrawLine(p, new Point(Math.Max(x - size, 0), y), new Point(Math.Min(x + size, w), y));
         g.DrawLine(p, new Point(x, Math.Max(y - size, 0)), new Point(x, Math.Min(y + size, w)));
      }

      private static void DrawHalfRect(Graphics g, Rectangle rect, Brush FillBrush, Pen Border)
      {
         if (FillBrush != null)
         {
            g.FillRectangle(FillBrush,
                            new Rectangle()
                            {
                               Height = rect.Height / 2,
                               Width = rect.Width / 2,
                               X = rect.X + rect.Width / 4,
                               Y = rect.Y + rect.Height / 4
                            });
         }
         if (Border != null)
         {
            g.DrawRectangle(Border,
                            new Rectangle()
                            {
                               Height = rect.Height / 2,
                               Width = rect.Width / 2,
                               X = rect.X + rect.Width / 4,
                               Y = rect.Y + rect.Height / 4
                            });
         }
      }
     
      private bool FindCubeSidebyColor(Color color, out Color FoundColor)
      {
         FoundColor = Color.White;
         if(CubeColors == null)
         {
            return false;
         }
         double MinDist = double.MaxValue;
         int MinIndex = 1;
         double cDist;
         for (int i = 0; i < CubeColors.Length; i++)
         {
            Color cubeColor = CubeColors[i];
            if ((cDist = distance(color, cubeColor)) < MinDist)
            {
               MinIndex = i + 1;
               MinDist = cDist;
            }
         }
         FoundColor = CubeColors[MinIndex - 1];
         return true;
      }
      private Color[] cubeColors;
      public Color[] CubeColors
      {
         get { return cubeColors; }
         set {
            cubeColors = value;
            InvokeOnPreviewChanged();
         }
      }

      public CubeInfo[] GetCubeColors()
      {
         unsafe
         {
            for (int i = 0; i < Cubes.Length; i++)
            {
               if(Cubes[i].Cube.Width == 0 ||Cubes[i].Cube.Height == 0)
                  continue;

               BitmapData bData = bmp.LockBits(Cubes[i].Cube, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

               var byteData =(byte*)bData.Scan0.ToPointer();
               var rgbData = (rgb*) byteData;
               var Stats = new RGBStats();

               for (int y = 0; y < Cubes[i].Cube.Height; y++)
               {
                  for (int x = 0; x < Cubes[i].Cube.Width; x++)
                  {
                     Stats.Add(rgbData[x]);
                  }
                  byteData += bData.Stride;
                  rgbData = (rgb*)byteData;
               }
               Cubes[i].Color = Stats.MeanColor();
               bmp.UnlockBits(bData);
            }

         }
         return Cubes;
      }

      protected double distance(rgb d1, rgb d2)
      {
         double d1d, d2d, d3d;
         d1d = d1.r - (double)d2.r;
         d2d = d1.g - (double)d2.g;
         d3d = d1.b - (double)d2.b;
         return Math.Sqrt(d1d * d1d + d2d * d2d + d3d * d3d);
      }

      protected double distance(Color d1, Color d2)
      {
         double d1d, d2d, d3d;
         d1d = d1.R - (double)d2.R;
         d2d = d1.G - (double)d2.G;
         d3d = d1.B - (double)d2.B;
         return Math.Sqrt(d1d * d1d + d2d * d2d + d3d * d3d);
      }
   }
   
}